package com.gulimall.auth.controller;

import com.alibaba.fastjson.TypeReference;
import com.common.constant.auth.AuthConstant;
import com.common.exception.BizCodeEnum;
import com.common.utils.R;
import com.common.vo.auth.MemberResponseVO;
import com.common.vo.auth.UserLoginVO;
import com.common.vo.auth.UserRegisterVO;
import com.gulimall.auth.feign.MemberService;
import com.gulimall.auth.feign.ThirdPartFeignService;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.validation.Valid;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * @Author: Ren(任佳宝)
 * @Description: LoginController
 * @Date: 2025/2/7 上午9:58
 * @Version: 1.0
 */

@Controller
@Slf4j
public class LoginController {

    @Autowired
    private ThirdPartFeignService thirdPartFeignService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private View error;
    @Qualifier("redisTemplate")
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MemberService memberService;

    @GetMapping("/sms/sendCode")
    @ResponseBody
    public R sendCode(@RequestParam("phone") String phone) {

        // ---------------------- 改进点2：常量提取 ----------------------
        final String key = AuthConstant.SMS_CODE_CACHE_PREFIX + phone;
        final int CODE_LENGTH = 5;         // 验证码长度
        final int EXPIRE_MINUTES = 5;      // Redis过期时间（分钟）
        final int RESEND_INTERVAL = 60;    // 重发间隔（秒）

        // ---------------------- 改进点3：线程安全随机数 ----------------------
        SecureRandom random = new SecureRandom();
        String code = String.format("%0" + CODE_LENGTH + "d", random.nextInt(90000) + 10000);
        String codeWithTimestamp = code + "_" + System.currentTimeMillis();

        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();

        try {
            // ---------------------- 改进点4：原子性操作建议 ----------------------
            // 使用 setIfAbsent 实现原子操作（根据业务需求选择）
            // Boolean result = ops.setIfAbsent(key, codeWithTimestamp, EXPIRE_MINUTES, TimeUnit.MINUTES);

            String existingCode = ops.get(key);
            if (existingCode == null) {
                // 首次发送：缓存验证码并发送
                ops.set(key, codeWithTimestamp, EXPIRE_MINUTES, TimeUnit.MINUTES);
                sendVerificationCode(phone, code);
            } else {
                // 检查重发间隔
                long lastSentTime = Long.parseLong(existingCode.split("_")[1]);
                if (System.currentTimeMillis() - lastSentTime < RESEND_INTERVAL * 1000L) {
                    return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), "验证码发送过于频繁");
                }

                // 更新验证码并重新发送
                ops.set(key, codeWithTimestamp, EXPIRE_MINUTES, TimeUnit.MINUTES);
                sendVerificationCode(phone, code);
            }

            return R.ok().put("expire", EXPIRE_MINUTES * 60); // 返回剩余秒数
        } catch (Exception e) {
            // ---------------------- 改进点5：异常处理 ----------------------
            log.error("短信发送失败 | 手机号:{} | 异常: {}", phone, e.getMessage());
            return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), "验证码发送失败");
        }
    }

    // ---------------------- 改进点6：抽取发送方法 ----------------------
    private void sendVerificationCode(String phone, String code) {
        try {
            thirdPartFeignService.sendCode(phone, code);
            log.info("短信发送成功 | 手机号:{} | 验证码:{}", phone, code);
        } catch (FeignException e) {
            log.error("第三方短信服务异常 | 状态码:{} | 响应:{}", e.status(), e.contentUTF8());
            throw new RuntimeException("短信服务调用失败", e);
        }
    }

    /**
     *
     * @param result
     * @param model 模拟重定向发送数据
     *
     * @return
     */
    @PostMapping("/register")
    public String regist(@Valid UserRegisterVO user, BindingResult result, RedirectAttributes model){

        if (result.hasErrors()) {
            Map<String, String> errors = new HashMap<>();
            result.getFieldErrors().forEach(err -> errors.put(err.getField(), err.getDefaultMessage()));
            model.addFlashAttribute("errors", errors);
            //校验出错,转发到注册页
            return "redirect:http://auth.gulimall.com/reg.html";// 采用重定向有一定防刷功能
            // 1、return "redirect:http://auth.gulimall.com/reg.html"【采用】 重定向Get请求【配合RedirectAttributes共享数据】
            // 2、return "redirect:http:/reg.html"                   【采用】 重定向Get请求，省略当前服务url【配合RedirectAttributes共享数据】
            // 3、return "redirect:/reg.html"                                重定向Get请求，使用视图控制器拦截请求并映射reg视图【配合RedirectAttributes共享数据】【bug：会以ip+port来重定向】
            // 4、return "forward:http://auth.gulimall.com/reg.html";        请求转发与当前请求方式一致（Post请求）【配合Model共享数据】【异常404：当前/reg.html不存在post请求】
            // 5、return "forward:http:/reg.html";                           请求转发与当前请求方式一致（Post请求），省略当前服务url 【配合Model共享数据】【异常404：当前/reg.html不存在post请求】
            // 6、return "forward:/reg.html";                                请求转发与当前请求方式一致（Post请求），使用视图控制器拦截请求并映射reg视图【配合Model共享数据】【异常405：Request method 'POST' not supported，视图控制器必须使用GET请求访问，而当前请求转发使用post方式，导致异常】
            // 7、return "reg";                                              视图解析器前后拼串查找资源返回【配合Model共享数据】
        }

        // 2.验证码校验
        String code = user.getCode();
        String redisCode = stringRedisTemplate.opsForValue().get(AuthConstant.SMS_CODE_CACHE_PREFIX + user.getPhone());
        if (StringUtils.isBlank(redisCode)) {
            // 验证码过期
            Map<String, String> errors = new HashMap<>();
            errors.put("code", "验证码失效");
            // 封装异常返回前端显示
            model.addFlashAttribute("errors", errors);// flash，session中的数据只使用一次
            return "redirect:http://auth.gulimall.com/reg.html";// 采用重定向有一定防刷功能
        }

        if (!code.equals(redisCode.split("_")[0])) {
            // 验证码错误
            Map<String, String> errMap = new HashMap<>();
            errMap.put("code", "验证码错误");
            // 封装异常返回前端显示
            model.addFlashAttribute("errors", errMap);// flash，session中的数据只使用一次
            return "redirect:http://auth.gulimall.com/reg.html";// 采用重定向有一定防刷功能
        }


        //注册
        R r = memberService.regist(user);
        if (r.getCode() == 0) {
            // 注册成功，重定向到登录页
            return "redirect:http://auth.gulimall.com/login.html";// 重定向
        } else {
            //　注册失败，封装异常
            HashMap<String, String> errors = new HashMap<>();
            errors.put("msg", r.getData("msg", new TypeReference<String>() {
            }));
            model.addFlashAttribute("errors", errors);// flash，session中的数据只使用一次
            return "redirect:http://auth.gulimall.com/reg.html";// 采用重定向有一定防刷功能
        }
    }

    /**
     * 登录接口
     */
    @PostMapping(value = "/login")
    public String login(UserLoginVO user,RedirectAttributes attributes){

        R login = memberService.login(user);

        if (login.getCode() == 0) {
            return "redirect:http://gulimall.com";
        }
        else{
            // 1.远程调用登录
            R r = memberService.login(user);
            if (r.getCode() == 0) {
                // 2.登录成功，设置session值
                MemberResponseVO data = r.getData(new TypeReference<MemberResponseVO>() {
                });
                //session.setAttribute(AuthConstant.LOGIN_USER, data);
                // 3.重定向，视图可以从session中拿到用户信息
                return "redirect:http://gulimall.com";
            } else {
                // 4.登录失败，封装异常信息重定向返回
                Map<String, String> errors = new HashMap<>();
                errors.put("msg", r.getData("msg", new TypeReference<String>() {}));
                attributes.addFlashAttribute("errors", errors);
                return "redirect:http://auth.gulimall.com/login.html";
            }
        }

    }

}
